package com.distribute.framework.core.web.util;

import com.distribute.framework.core.web.helper.Utils;
import java.io.BufferedReader;
import java.io.CharArrayReader;
import java.io.CharArrayWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.UnsupportedEncodingException;
import java.io.Writer;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;


import com.distribute.framework.core.web.helper.WebHelper;

/**
 * 单JS文件加密
 * @author uiu
 */
public class JSEncode {	
	
    /**
     * @notes 字符串转化为十六进制字符串
     * @param bytes
     * @return String
     */
    public static String hex(String strString) {
    	if(WebHelper.empty(strString))
    		return "";
    	StringBuffer strHex = new StringBuffer();  
    	for(int i=0;i<strString.length();i++){
    		Integer charCode = new Integer(strString.charAt(i));//取unicode码
    		charCode = charCode+23; //unicode码变种
    		strHex.append(Utils.zerofill(charCode.toString(charCode, 16), 4));////转成十六进制,4位对齐	
    	}
        return strHex.toString().toUpperCase();
    }
		
	
	/**
	 * 合并js文件
	 * @param bWindow 
	 * @param array
	 * bLaji 是否要插入垃圾代码
	 */
	public static String encode(String jsContent,String PREFIX, Boolean bWindow){
		try{
		
			String windowJS = "var "+PREFIX+"0F1=window,"+PREFIX+"0=0,"+PREFIX+"0F2='length',"+PREFIX+"0F3='fromCharCode',"+PREFIX+"0F4='substr',"+PREFIX+"0F5='parseInt',"+PREFIX+"0F6='String',"+PREFIX+"0F7='keycache';"+PREFIX+"0F1["+PREFIX+"0F7]={};";
			
			//声明30个window克隆对象,动态运行js,在浏览器中命名window
			Map<Integer,String> windows=new HashMap();		
			String evalWindow="";
			for(int i=0;i<30;i++){
				String windowFlag = PREFIX+"3"+ Utils.hex(""+i);
				windows.put(i,windowFlag);
				evalWindow+=""+PREFIX+"0F1."+windowFlag+"="+PREFIX+"0F1;\n";
			}
			evalWindow=""+PREFIX+"0F1["+PREFIX+"0F8({"+PREFIX+hex("eval")+":"+PREFIX+"0})](\"{\"+"+PREFIX+"0F8({"+PREFIX+hex(evalWindow)+":"+PREFIX+"0})+\"}\");";
			
			
			//声明30个十六进制转字符串函数名,动态运行js,在浏览器中克隆该函数名
			Map<Integer,String> xehs=new HashMap();
			String evalXeh="";
			for(int i=0;i<30;i++){
				String xehFlag = PREFIX+"2"+ Utils.hex(""+i);
				xehs.put(i,xehFlag);
				evalXeh+=""+PREFIX+"0F1."+xehFlag+"="+PREFIX+"0F8;\n";
			}
			evalXeh=""+PREFIX+"0F1["+PREFIX+"0F8({"+PREFIX+hex("eval")+":"+PREFIX+"0})](\"{\"+"+PREFIX+"0F8({"+PREFIX+hex(evalXeh)+":"+PREFIX+"0})+\"}\");";
			
			Map<String,String> vids = new HashMap();//变量名ID列表
				
			//加密的lte.js
			CharArrayWriter encodeout = new CharArrayWriter();
			
			//写入加密过的全局函数
			if(bWindow){
				//写入window.js
				encodeout.write(windowJS);
				newLine(encodeout);	
				
				
				//js十六进制转字符串函数 
				encodeout.write("function "+PREFIX+"0F8("+PREFIX+"0FA){ for(var "+PREFIX+"0 in "+PREFIX+"0FA){"+PREFIX+"0FA="+PREFIX+"0["+PREFIX+"0F4](1);} if(typeof("+PREFIX+"0F1["+PREFIX+"0F7][\"\"+"+PREFIX+"0FA])!=\"undefined\"){return "+PREFIX+"0F1["+PREFIX+"0F7][\"\"+"+PREFIX+"0FA];} var "+PREFIX+"0FB  = ''; for(var F6F6=0;F6F6<"+PREFIX+"0FA["+PREFIX+"0F2]/4;F6F6++){"+PREFIX+"0FB+="+PREFIX+"0F1["+PREFIX+"0F6]["+PREFIX+"0F3]("+PREFIX+"0F1["+PREFIX+"0F5]("+PREFIX+"0FA["+PREFIX+"0F4](F6F6*4,4),16)-23);}if("+PREFIX+"0FB["+PREFIX+"0F2]<10000){"+PREFIX+"0F1["+PREFIX+"0F7][\"\"+"+PREFIX+"0FA]="+PREFIX+"0FB;}return "+PREFIX+"0FB;};");
				newLine(encodeout);
				encodeout.write(evalXeh);
				newLine(encodeout);
				
				
				//在window.js后面写入克隆window名
				encodeout.write(evalWindow);
				newLine(encodeout);
				
			}
			
			//去除块级注释
			jsContent = jsContent.replaceAll("/\\*[\\d\\D]*?\\*/", "");
			
			//提取动态代码块
			Map<String,String> blocks = new HashMap();
			Matcher blockMatch = Pattern.compile("@begin<([^>]+)>([\\d\\D]*?)@end").matcher(jsContent);
			while(blockMatch.find()){
				String name = blockMatch.group(1);
				String code = blockMatch.group(2);			
				blocks.put(name, code);
			}	
			jsContent = jsContent.replaceAll("@begin[\\d\\D]*?@end", "");
				
			
			{
				BufferedReader reader=new BufferedReader(new CharArrayReader(jsContent.toCharArray()));
				//out.write("//"+jsFile+"\r\n");
				String strEnLine=null,strUnLine=null;
				int linecount = 0;
				while((strEnLine=reader.readLine())!=null){
					strUnLine = strEnLine;
					strEnLine = " "+Utils.trim(strEnLine)+" ";
					//strEnLine = strEnLine.replace("console.log", "//console.log");
					strEnLine = format$(strEnLine,vids,PREFIX);//混淆变量名	
					strEnLine = formatAT(strEnLine,blocks);//把@数字引用文本块转eval(字串函数)
					strEnLine = formatInclude(strEnLine);//处理文件导入
					strEnLine = formatS(strEnLine,xehs.get(Utils.rand(xehs.size())),PREFIX);//把字符串转16进制 "+PREFIX+"0F8:js十六进制转字串函数
					strEnLine = formatC(strEnLine,xehs.get(Utils.rand(xehs.size())),PREFIX);//把调用的函数名改成xxx[十六进制] 这种模式 "+PREFIX+"0F8:js十六进制转字串函数
					strEnLine = formatW(strEnLine,xehs.get(Utils.rand(xehs.size())),windows.get(Utils.rand(windows.size())),PREFIX);//修改全局函数调用方式 "+PREFIX+"0F8:js十六进制转字串函数 "+PREFIX+"0F1:window标识
					strEnLine = formatN(strEnLine);//去注释
					
					if(Utils.empty(strEnLine))
						continue;			
					encodeout.write(strEnLine);
					newLine(encodeout);
				}		
			}	
			encodeout.close();
			return encodeout.toString();
		}catch(Exception ex){
			ex.printStackTrace();
			return null;
		}
	}


	//写入换行符
	private static void newLine(Writer out) throws IOException{
		out.write("\n");
	}

	
	//去除注释
	private static String formatN(String jsString) {
		if(Utils.empty(jsString))return null;		
		return jsString.indexOf("//")>-1 ? jsString.substring(0,jsString.indexOf("//")) : jsString;
	}
	
	
	
	//把@数字引用文本块转16进制 "+PREFIX+"0F8:js十六进制转字串函数
	private static String formatAT(String jsString,Map<String,String> blocks) {		
		if(Utils.empty(jsString))return null;
		String convertString = "";
		int last = 0;
		Matcher match = Pattern.compile("(?i)(@<([^>]+)>)").matcher(jsString);		
		while(match.find()){
			String sFlag = match.group(2);
			String sValue = blocks.get(sFlag);
			//sValue = Utils.js(sValue);
			//sValue = escape(sValue);
			//Utils.trace(sValue);
			if(sValue==null)
				Utils.trace(match.group(1));
			sValue = sValue.replaceAll("\\\\", "\\\\\\\\");
			sValue = sValue.replaceAll("\"", "\\\\\\\"");
			//sValue = sValue.replaceAll("\r", "\\r");
			//sValue = sValue.replaceAll("\n", "\\n");
			convertString+=jsString.substring(last,match.start(1));		
			convertString+="\""+sValue+"\"";
			//Utils.trace(sValue);
			last = match.end(1);
		}	
		convertString+=jsString.substring(last,jsString.length());
		return convertString;
	}
	
	
	//把@数字引用文本块转16进制 "+PREFIX+"0F8:js十六进制转字串函数
	private static String formatInclude(String jsString) throws Exception {		
		if(Utils.empty(jsString))return null;
		String convertString = "";
		int last = 0;
		Matcher match = Pattern.compile("(?i)(#<([^>]+)>)").matcher(jsString);		
		while(match.find()){
			String filename = match.group(2);
			String sValue = Utils.readFile(filename);
			//sValue = Utils.js(sValue);
			//sValue = escape(sValue);
			sValue = sValue.replaceAll("\\\\", "\\\\\\\\");
			sValue = sValue.replaceAll("\"", "\\\\\\\"");
			//sValue = sValue.replaceAll("\r", "\\r");
			//sValue = sValue.replaceAll("\n", "\\n");
			convertString+=jsString.substring(last,match.start(1));		
			convertString+="\""+sValue+"\"";
			//Utils.trace(sValue);
			last = match.end(1);
		}	
		convertString+=jsString.substring(last,jsString.length());
		return convertString;
	}
	
	
	
	
	//把字符串转十六进制
	private static String formatS(String jsString,String jsXehFuncName,String PREFIX) {		
		if(Utils.empty(jsString))return null;
		
		if(jsString.indexOf("\\\\\\\"")>-1)
			jsString = jsString.replace("\\\\\\\"", "[操操]");
		if(jsString.indexOf("\\\\n")>-1)
			jsString = jsString.replace("\\\\n", "[日日]");
		
		if(jsString.indexOf("\\\"")>-1)
			jsString = jsString.replace("\\\"", "[操]");
		if(jsString.indexOf("\\n")>-1)
			jsString = jsString.replace("\\n", "[日]");
		
		String convertString = "";
		int last = 0;
		Matcher match = Pattern.compile("(?i)(\\\"[^\\\"]*?[\\\"])").matcher(jsString);		
		while(match.find()){
			String sValue = match.group(1);
			sValue=sValue.substring(1);//去除前面的引号
			sValue=sValue.substring(0,sValue.length()-1);//去除后面的引号
			convertString+=jsString.substring(last,match.start(1));
			if(jsString.substring(match.end(1)).trim().startsWith(":")){
				if(jsString.substring(0, match.start(1)).trim().endsWith("?")){
					convertString+=jsXehFuncName+"({F"+hex(sValue)+":"+PREFIX+"0})";
				}else{
					convertString+="\""+sValue+"\""; 
				}
			}else{
				convertString+=jsXehFuncName+"({F"+hex(sValue)+":"+PREFIX+"0})";
			}
				
			last = match.end(1);
		}	
		convertString+=jsString.substring(last,jsString.length());	
		
		
		if(convertString.indexOf(hex("[操操]"))>-1)
			convertString = convertString.replaceAll(hex("[操操]"), hex("\\\""));		
		if(convertString.indexOf("[操操]")>-1)
			convertString = convertString.replace("[操操]", "\\\\\\\"");
		
		if(convertString.indexOf(hex("[日日]"))>-1)
			convertString = convertString.replaceAll(hex("[日日]"), hex("\\\n"));		
		if(convertString.indexOf("[日日]")>-1)
			convertString = convertString.replace("[日日]", "\\\\n");
		
		
		
		
		if(convertString.indexOf(hex("[操]"))>-1)
			convertString = convertString.replaceAll(hex("[操]"), hex("\""));		
		if(convertString.indexOf("[操]")>-1)
			convertString = convertString.replace("[操]", "\\\"");
		
		if(convertString.indexOf(hex("[日]"))>-1)
			convertString = convertString.replaceAll(hex("[日]"), hex("\n"));		
		if(convertString.indexOf("[日]")>-1)
			convertString = convertString.replace("[日]", "\\n");
		
		
		
		
		
		
		return convertString;
	}
	
	
	//把以$开头的变量名加密
	private static String format$(String jsString, Map<String,String> vids,String PREFIX) {		
		if(Utils.empty(jsString))return null;
		String convertString = "";
		int last = 0;
		Matcher match = Pattern.compile("(\\$?\\$[_a-zA-Z0-9]+)").matcher(jsString);
		while(match.find()){
			String vName = match.group(1);			
			convertString+=jsString.substring(last,match.start(1));		
			
			//转换后的变量名
			String converName = vids.get(vName);
			if(Utils.empty(converName)){
				//如果之前没声明过，按已取的名字增量
				converName = Utils.hex(""+(vids.size()+1));
				vids.put(vName, converName);
			}
			converName = PREFIX+"0"+converName;//前面加个F0防止以数字开头			
			convertString+=converName;
			last = match.end(1);
		}
		//System.out.println(vids.size());
		convertString+=jsString.substring(last,jsString.length());	
		return convertString;
	}
	
	
	//把调用的函数名改成xxx[十六进制] 这种模式
	private static String formatC(String jsString,String jsXehFuncName,String PREFIX) {
		if(Utils.empty(jsString))return null;
		String convertString = "";
		int last = 0;
		Matcher match = Pattern.compile("(\\.[_a-zA-Z][_a-zA-Z0-9]*)").matcher(jsString);
		while(match.find()){
			String vName = match.group(1);			
			convertString+=jsString.substring(last,match.start(1));
			String converName = "";
			if(vName.startsWith("."+PREFIX+"0")){//已经被混淆过了
				converName = vName;
			}else{
				vName=vName.substring(1);//去除前面的小点
				//System.out.println(vName);
				vName = "["+jsXehFuncName+"({F"+hex(vName)+":"+PREFIX+"0})]";//改成xxx[十六进制] 这种模式	
				converName = vName;
			}
			convertString+=converName;
			last = match.end(1);
		}	
		convertString+=jsString.substring(last,jsString.length());	
		
		return convertString;
	}
	//把调用的函数名改成xxx[十六进制] 这种模式
	private static String formatW(String jsString,String jsXehFuncName,String windowFlag,String PREFIX) {
		if(Utils.empty(jsString))return null;
		String convertString = "";
		int last = 0;
		Matcher match = Pattern.compile("(document|console|setTimeout|setInterval|clearInterval|Math|alert|Number)").matcher(jsString);
		while(match.find()){
			String vName = match.group(1);			
			convertString+=jsString.substring(last,match.start(1));
			String converName = windowFlag+"["+jsXehFuncName+"({"+PREFIX+""+hex(vName)+":"+PREFIX+"0})]";//改成window[十六进制] 这种模式	
			convertString+=converName;
			last = match.end(1);
		}	
		convertString+=jsString.substring(last,jsString.length());
		convertString = convertString.replace("window", windowFlag);
		return convertString;
	}
	
	
	// java版本的escape和unescape函数

	
	 public static String  escape (String src)
	 {
	  int i;
	  char j;
	  StringBuffer tmp = new StringBuffer();
	  tmp.ensureCapacity(src.length()*6);

	  for (i=0;i<src.length() ;i++ )
	  {

	   j = src.charAt(i);

	   if (Character.isDigit(j) || Character.isLowerCase(j) || Character.isUpperCase(j))
	    tmp.append(j);
	   else
	    if (j<256)
	    {
	    tmp.append( "%" );
	    if (j<16)
	     tmp.append( "0" );
	    tmp.append( Integer.toString(j,16) );
	    }
	    else
	    {
	    tmp.append( "%u" );
	    tmp.append( Integer.toString(j,16) );
	    }
	  }
	  return tmp.toString();
	 }

	 public static String  unescape (String src)
	 {
	  StringBuffer tmp = new StringBuffer();
	  tmp.ensureCapacity(src.length());
	  int  lastPos=0,pos=0;
	  char ch;
	  while (lastPos<src.length())
	  {
	   pos = src.indexOf("%",lastPos);
	   if (pos == lastPos)
	    {
	    if (src.charAt(pos+1)=='u')
	     {
	     ch = (char)Integer.parseInt(src.substring(pos+2,pos+6),16);
	     tmp.append(ch);
	     lastPos = pos+6;
	     }
	    else
	     {
	     ch = (char)Integer.parseInt(src.substring(pos+1,pos+3),16);
	     tmp.append(ch);
	     lastPos = pos+3;
	     }
	    }
	   else
	    {
	    if (pos == -1)
	     {
	     tmp.append(src.substring(lastPos));
	     lastPos=src.length();
	     }
	    else
	     {
	     tmp.append(src.substring(lastPos,pos));
	     lastPos=pos;
	     }
	    }
	  }
	  return tmp.toString();
	 }

	
}